# first, let us build a list and return it
def list_n(n):
    '''build a list'''
    counter, nums = 0, []
    while counter < n:
        nums.append(counter)
        counter += 1
    return nums

sum_of_list_n = sum(list_n(1000000))
print(sum_of_list_n)

# the code is quite simple and straightforward,但实际它在内存里构建了全部list。
# 创建一个包含100万个元素的列表，不仅占用很大的存储空间，如果我们仅仅需要访问前面几个元素，那后面绝大多数元素占用的空间都白白浪费了。
# 所以，如果列表元素可以按照某种算法推算出来，那我们是否可以在循环的过程中不断推算出后续的元素呢？
# 这样就不必创建完整的list，从而节省大量的空间。在Python中，这种一边循环一边计算的机制，称为生成器：generator。
# https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128
class list_nn:
    def __init__(self, n) -> None:
        self.n = n
        self.counter = 0
    def __iter__(self):
        return self
    # python3 compatibility
    def __next__(self):
        return self.next()
    def next(self):
        if self.counter < self.n:
            cur, self.counter = self.counter, self.counter+1
            return cur
        raise StopIteration()

sum_of_list_nn = sum(list_nn(1000000))
print(sum_of_list_nn)

# a generator that yields items
def listn(n):
    num = 0
    while num < n:
        yield num
        num += 1
sum_of_listn = sum(listn(1000000))
print(sum_of_listn)


# 要创建一个generator，有很多种方法。
# 1. 第一种方法很简单，只要把一个列表生成式的[]改成()，就创建了一个generator
list_a = [x for x in range(10)]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list_a) 

gen_a = (x for x in range(10))
# <generator object <genexpr> at 0x1022ef630>
print(gen_a)
# 一步一步地输出元素
print(gen_a.__next__()) 
print(next(gen_a))
print(next(gen_a))
# generator保存的是算法，每次调用next(g)，就计算出g的下一个元素的值，
# 直到计算到最后一个元素，没有更多的元素时，抛出StopIteration的错误。
# 当然，上面这种不断调用next(g)实在是太变态了，正确的方法是使用for循环，因为generator也是可迭代对象：
for x in gen_a:
    print(x)
# 所以，我们创建了一个generator后，基本上永远不会调用next()，而是通过for循环来迭代它，并且不需要关心StopIteration的错误。

# 2. 定义generator的另一种方法, 用yield
print('=====yield====')
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        # 如果一个函数定义中包含yield关键字，那么这个函数就不再是一个普通函数，而是一个generator函数，调用一个generator函数将返回一个generator：
        # 变成generator的函数，在每次调用next()的时候执行，遇到yield语句返回，再次执行时从上次返回的yield语句处继续执行。
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
gen_f = fib(6)
for y in gen_f:
    print(y)
print('=====或者用yield-next====')
# print(next(gen_f))

# 杨辉三角
print('====triangles===')
def triangles():

    list_a = [1];

    while True:

        yield list_a;

        for i in range(1,len(list_a)):

            list_a[i] = list_b[i-1]+list_b[i];

        list_a.append(1);

        list_b = list_a[:];

tl = triangles();

for j in range(8):

    print(next(tl));

# triangles
def triangles1():
    r=[1]
    while True:
        yield(r)
        r = [1]+[r[i]+r[i+1] for i in range(len(r)-1)]+[1]
tr = triangles1()

for j in range(8):
    print(next(tr)); 